exports.__esModule = true;

var _assert = require('assert');

var _assert2 = _interopRequireDefault(_assert);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }

const defaultEntity = { //树表实体key名标识
    ID: 'id', //主键id
    PID: 'parent_id', //父级id
    PIDS: 'parent_ids', //父级ids
    LEVEL: 'level', //级别
    CHILDREN: 'children',
    LEAF: 'tree_leaf', //是否是叶子节点
    TREE_NAMES: 'tree_names', //全路径名称
    TREE_NAME: 'tree_name', //节点名称
    DELETE_WAY: 1, //删除方式：0修改状态，不删除数据;1.删除数据;
    STATUS: 'status' //状态

};

/**
 *  Tree基类
 */
class TreeService extends think.Service {
    constructor(config) {
        super();
        Object.assign(defaultEntity, config);
    }

    /**
     * 用于树表新增和更新当前节点的所有pids,level,pid,以及所有子子孙孙的pids,　支持任意一级的更新，支持父子节点交换。
     * @param tableName  表名 string
     * @param parentEntity 父对象实体 Object
     * @param childEntity 要保存或更新的子对象实体 Object
     * @param options 选项对象
     */
    saveTree(tableName, parentEntity, childEntity, options) {
        var _this = this;

        return _asyncToGenerator(function* () {
            (0, _assert2.default)(typeof tableName === 'string', 'tableName must be a string');
            (0, _assert2.default)(parentEntity instanceof Object, 'parentEntity must be a Object');
            (0, _assert2.default)(childEntity instanceof Object, 'childEntity must be a Object');
            const tableModel = _this.db[tableName];
            const ID = defaultEntity.ID;
            const PID = defaultEntity.PID;
            const PIDS = defaultEntity.PIDS;
            const LEVEL = defaultEntity.LEVEL;
            const TREE_NAMES = defaultEntity.TREE_NAMES;
            const TREE_NAME = defaultEntity.TREE_NAME;
            const TREE_LEAF = defaultEntity.TREE_LEAF;
            //获取父对象和子对象属性
            const { [ID]: pEntityId, [PID]: pEntityPid, [PIDS]: pEntityPids, [TREE_NAMES]: pEntityTreeNames = '' } = parentEntity;
            const { [ID]: cEntityId, [PID]: cEntityPid, [PIDS]: cEntityPids, [TREE_NAME]: cEntityTreeName = '' } = childEntity;
            // 获取子对象修改前的parentIds和pId，用于更新子节点的parentIds和Pid
            const oldParentIds = cEntityPids;
            const oldParentId = cEntityPid;

            // 如果目标父节点是自己，则不处理PIDS、PID、LEVEL
            if (pEntityId === cEntityId) {
                delete childEntity[PID];
                delete childEntity[PIDS];
                delete childEntity[LEVEL];
                return tableModel.upsert(childEntity);
            }
            // 如果没有设置父节点，则代表为根节点
            if (pEntityPid === null || pEntityPid === '' || pEntityPid === undefined) {
                childEntity[PID] = 0;
                childEntity[PIDS] = '0,';
            } else {
                //获取父对象实体的parent_ids并且拼接parent_ids
                let updateParentIds = '';
                //如果存在父子节点交换，踢出在树路径里自己的id
                if (pEntityPids && pEntityPids.includes(cEntityId)) {
                    //更新当前节点的parentIds
                    updateParentIds = `${pEntityPids}${pEntityId},`.replace(`${cEntityId},`, '');
                } else {
                    updateParentIds = `${pEntityPids}${pEntityId},`;
                }

                childEntity[PIDS] = updateParentIds;
                childEntity[PID] = pEntityId;
                childEntity[LEVEL] = updateParentIds.split(',').length - 1;
            }
            if (cEntityTreeName) {
                //构造节点全路径名称
                pEntityTreeNames ? childEntity[TREE_NAMES] = pEntityTreeNames + '/' + cEntityTreeName : childEntity[TREE_NAMES] = cEntityTreeName;
            }

            //如果是新增操作,设置当前节点为末级节点，同时设置父节点为非末级节点
            if (!cEntityId) {
                childEntity[TREE_LEAF] = 1;
                tableModel.update({ [TREE_LEAF]: 0 }, {
                    where: {
                        [ID]: pEntityId
                    }
                });
            }
            //保存或更新当前实体
            const upsertResult = tableModel.upsert(childEntity);

            //更新当前修改节点下所有子节点的parentIds
            if (cEntityId && pEntityPids) {
                (0, _assert2.default)(oldParentIds != undefined, 'parentIds must be not empty');
                let sql = `SELECT ${ID},${PID},${PIDS} FROM ${tableName} WHERE ${PIDS} like '%${oldParentIds}${cEntityId},%'`;
                const list = yield _this.CrudService.querySql(sql);
                if (list.length > 0) {
                    for (let value of list) {
                        if (value[PIDS] && oldParentIds) {
                            let obj = {};
                            //如果存在父子节点交换,踢出在树路径里自己的id
                            if (pEntityPids.includes(cEntityId)) {
                                obj[PIDS] = value[PIDS].replace(`${cEntityId},`, '');
                                //更新交换节点前的pid
                                if (value[PID] === cEntityId) {
                                    obj[PID] = oldParentId;
                                }
                            } else {
                                obj[PIDS] = value[PIDS].replace(oldParentIds, cEntityPids);
                            }
                            obj[LEVEL] = obj[PIDS].split(',').length - 1;
                            tableModel.update(obj, {
                                where: {
                                    id: value[ID]
                                }
                            });
                        }
                    }
                }
            }
            return upsertResult;
        })();
    }

    /**
     *  全表扫描，用于重新生成全表的PIDS
     *
     * @param tableName  表名 string
     */
    setAllParentIds(tableName) {
        var _this2 = this;

        return _asyncToGenerator(function* () {

            (0, _assert2.default)(typeof tableName === 'string', 'tableName must be a string');
            const tableModel = _this2.db[tableName];
            //获得表中所有 数据
            let tableDataList = yield tableModel.findAll();
            //将数组构造为id为key的对象
            let idField = defaultEntity.ID;
            let parentIDField = defaultEntity.PID;
            let parentIDSField = defaultEntity.PIDS;
            let dataObj = {};
            tableDataList.map(function (item) {
                dataObj[item[idField]] = item.dataValues;
            });
            //遍历数组，并查出parent_ids
            tableDataList.map(function (item) {
                let parent_ids = item[parentIDField] + ',';
                let isHasParent = true;
                let parent_id = item[parentIDField];
                while (isHasParent) {
                    let parentObj = {};
                    parentObj = dataObj[parent_id];
                    while (parentObj != undefined) {
                        parent_ids = parentObj.parent_id + ',' + parent_ids;
                        parentObj = dataObj[parentObj.parent_id];
                    }
                    isHasParent = false;
                }
                item[parentIDSField] = parent_ids;
                return item;
            });
            //捕捉promise异常，不捕捉经常报ResourceRequest timed out错误
            try {
                for (let i = 0; i < tableDataList.length; i++) {
                    yield tableModel.update({ [parentIDSField]: tableDataList[i].dataValues[parentIDSField] }, { where: { [idField]: tableDataList[i][idField] } });
                }
            } catch (e) {
                console.log(e);
            }
        })();
    }

    /**
     *  全表扫描，用于重新生成全表的LEVEL
     *
     * @param tableName  表名 string
     */
    setLevel(tableName) {
        var _this3 = this;

        return _asyncToGenerator(function* () {
            (0, _assert2.default)(typeof tableName === 'string', 'tableName must be a string');
            const tableModel = _this3.db[tableName];
            //获得表中所有 数据
            let tableDataList = yield tableModel.findAll();
            let idField = defaultEntity.ID;
            let parentIDSField = defaultEntity.PIDS;
            let levelField = defaultEntity.LEVEL;
            //遍历数组，并查出parent_ids
            tableDataList.map(function (item) {
                let parent_idsArr = item[parentIDSField].split(',');
                item[levelField] = parent_idsArr.length - 1;
            });
            //捕捉promise异常，不捕捉经常报ResourceRequest timed out错误
            try {
                for (let i = 0; i < tableDataList.length; i++) {
                    yield tableModel.update({ [levelField]: tableDataList[i][levelField] }, { where: { [idField]: tableDataList[i][idField] } });
                }
            } catch (e) {
                console.log(e);
            }
        })();
    }

    /**
     * 格式转树状结构
     * @param   {Array}  a 原数据
     */
    transData(a) {
        let r = [],
            hash = {},
            id = defaultEntity.ID,
            pid = defaultEntity.PID,
            children = defaultEntity.CHILDREN,
            i = 0,
            j = 0,
            len = a.length;
        for (; i < len; i++) {
            hash[a[i][id]] = a[i];
        }
        for (; j < len; j++) {
            var aVal = a[j],
                hashVP = hash[aVal[pid]];
            if (hashVP) {
                !hashVP[children] && (hashVP[children] = []);
                hashVP[children].push(aVal);
            } else {
                r.push(aVal);
            }
        }
        return r;
    }

    /**
     * 根据表名和ID获取当前节点的子子孙孙
     * @param tableName  String  表名 (必传)
     * @param id      String   id  (必传)
     * @param fields Array  fields,默认['*']
     */
    findGrandchildrens(tableName, id, fields = ['*']) {
        const { ID, PIDS } = defaultEntity;
        fields = fields.join();
        const sql = `SELECT ${fields} FROM ${tableName} WHERE ${PIDS} LIKE CONCAT('%', (SELECT ${PIDS} FROM ${tableName} WHERE ${ID}=${id}),'${id},' '%')`;
        return this.CrudService.querySql(sql);
    }

    /**
     * 根据表名和ID获取当前节点的子子孙孙（包含自己）
     * @param tableName  String  表名 (必传)
     * @param id      String   id  (必传)
     * @param fields Array  fields,默认['*']
     */
    findGrandchildrensAndSelf(tableName, id, fields = ['*']) {
        const { ID, PIDS } = defaultEntity;
        fields = fields.join();
        const sql = `SELECT ${fields} FROM ${tableName} WHERE ${PIDS} LIKE CONCAT('%', (SELECT ${PIDS} FROM ${tableName} WHERE ${ID}=${id}),'${id},' '%') or ${ID}=${id}`;
        return this.CrudService.querySql(sql);
    }

    /**
     * 根据表名和ID获取当前节点下的所有子节点(不包含孙孙子子)
     */
    findChildrens(tableName, id, fields = ['*']) {
        const { ID, PIDS, PID } = defaultEntity;
        fields = fields.join();
        const sql = `SELECT ${fields} FROM ${tableName} WHERE ${PID}=${id}`;
        return this.CrudService.querySql(sql);
    }

    /**
     * 根据表名和ID删除当前节点及其下面的所有子节点
     */
    deleteParentAndChildrenNode(tableName, id) {
        var _this4 = this;

        return _asyncToGenerator(function* () {
            (0, _assert2.default)(typeof tableName === 'string', 'tableName must be a string');
            const tableModel = _this4.db[tableName];
            const { ID, PIDS, DELETE_WAY, STATUS } = defaultEntity;
            const Op = _this4.db.sequelize.Op;
            try {
                if (DELETE_WAY) {
                    yield tableModel.destroy({
                        where: {
                            [Op.or]: {
                                [PIDS]: {
                                    [Op.like]: '%' + id + ','
                                },
                                [ID]: id
                            }
                        }
                    });
                } else {
                    yield tableModel.update({ [STATUS]: 1 }, {
                        where: {
                            [Op.or]: {
                                [PIDS]: {
                                    [Op.like]: '%' + id + ','
                                },
                                [ID]: id
                            }
                        }
                    });
                }
            } catch (error) {
                return _this4.exceptionService.handleError(error);
            }
        })();
    }
}

exports.default = TreeService;